package org.datascooter.impl;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.datascooter.cache.ConnectionManager;
import org.datascooter.exception.SnipManagerException;
import org.datascooter.inface.IContextConnector;
import org.datascooter.inface.IDataSource;
import org.datascooter.inface.ISnipManager;
import org.datascooter.meta.Column;
import org.datascooter.meta.MetaTable;
import org.datascooter.utils.SnipUtils;

public class SnipManager implements ISnipManager {

	private static final String RESULT_SIZE = " Result size : ";
	private static final String ERROR_BY_SNIP = "***Error by snip: ";
	private static final String SCHEME = "    scheme - ";
	private static final String TABLE2 = " -table -";
	private static final String ERROR_BY_ROLLBACK_CONNECTION = "***Error by rollback connection";
	private static final String ERROR_BY_COMMIT_CONNECTION = "***Error by commit connection ";
	private static final String GET_NEXT_EXCEPTION = "  -getNextException ";
	private static final String SNIP_MANAGER_ERROR = "***SnipManager_Error --  ";
	// private ConnectionPoolManager poolMgr;
	private final IContextConnector connector;
	private String url;
	private String tranId = null;
	private boolean requestedCommit = false;
	private SnipExecutor tranExecutor;
	private boolean supportPagination;
	private boolean callParameterMetadata;
	private boolean isShowSQL = false;
	private boolean isShowRetSQL = false;
	private Logger logger = Logger.getLogger(SnipManager.class.getName());

	public SnipManager(IContextConnector connector) throws SnipManagerException {
		this.connector = connector;
		supportPagination = connector.isSupportPagination();
		callParameterMetadata = connector.isCallParameterMetadata();
		url = connector.getDataSource().getURL();
		tranExecutor = new SnipExecutor(supportPagination, callParameterMetadata);
	}

	public String getName() {
		return url;
	}

	@Override
	public Snip retrieve(Snip snip) throws SnipManagerException {
		execute(snip);
		if (isShowRetSQL) {
			logger.info(SnipUtils.snipToSQLString(snip) + RESULT_SIZE + snip.getData().length);
		}
		return snip;
	}

	@Override
	public void execute(Snip snip) throws SnipManagerException {
		if (isShowSQL) {
			logger.info(SnipUtils.snipToSQLString(snip));
		}
		if (tranId != null) {
			synchronized (tranExecutor) {
				try {
					if (requestedCommit) {
						commit();
					} else if (snip.tranId != null && (!snip.tranId.equals(tranId))) {
						rollback();
					}
					tranExecutor.prepare(url);
					tranExecutor.execute(snip);
					if (requestedCommit) {
						commit();
					}
					tranId = snip.tranId;
				} catch (SQLException e) {
					rollback();
					logError(snip, e);
				} catch (Throwable e) {
					rollback();
					logError(snip, e);
				}
			}
		} else {
			try {
				new SnipSelfExecutor(supportPagination, callParameterMetadata, ConnectionManager.getConnectionBundle(url)).execute(snip);
			} catch (Throwable e) {
				logError(snip, e);
			}
			return;
		}
	}

	private void logError(Snip snip, Throwable ex) throws SnipManagerException {
		ex.printStackTrace();
		String msg = SNIP_MANAGER_ERROR + SnipUtils.snipToString(snip);
		logger.log(Level.SEVERE, msg, ex);
		throw new SnipManagerException(ERROR_BY_SNIP + SnipUtils.snipToString(snip), ex);
	}

	private void logError(Snip snip, SQLException ex) throws SnipManagerException {
		ex.printStackTrace();
		String msg = SNIP_MANAGER_ERROR + SnipUtils.snipToString(snip);
		SQLException nextException = ex.getNextException();
		if (nextException != null) {
			logger.log(Level.SEVERE, msg + GET_NEXT_EXCEPTION, nextException);
		} else {
			logger.log(Level.SEVERE, msg, ex);
		}
		throw new SnipManagerException(ERROR_BY_SNIP + SnipUtils.snipToString(snip), ex);
	}

	@Override
	public Date getCurrentDate() {
		return new Date();
	}

	public IDataSource getDataSource() {
		return connector.getDataSource();
	}

	@Override
	public void beginTransaction(String transactionId) {
		tranId = transactionId;
	}

	@Override
	public void commit() throws SnipManagerException {
		try {
			tranExecutor.commit();
		} catch (Exception e) {
			throw new SnipManagerException(ERROR_BY_COMMIT_CONNECTION, e);
		}
		tranId = null;
		requestedCommit = false;
	}

	@Override
	public void rollback() throws SnipManagerException {
		try {
			tranExecutor.rollback();
		} catch (Exception e) {
			throw new SnipManagerException(ERROR_BY_ROLLBACK_CONNECTION, e);
		}
		tranId = null;
		requestedCommit = false;
	}

	@Override
	public void requestCommit() {
		requestedCommit = true;
	}

	@Override
	public void close() {
		ConnectionManager.unRegister(connector);
		connector.close();
	}

	@Override
	public Map<String, Column> getCurrentColumns(String shemaName, String tableName) throws SnipManagerException {
		try {
			return new ServerTableManager(connector.getConnection()).getCurrentColumns(shemaName, tableName);
		} catch (SQLException e) {
			throw new SnipManagerException(url, e);
		}
	}

	@Override
	public boolean isTableExists(String schema, String tableName) throws SnipManagerException {
		try {
			return new ServerTableManager(connector.getConnection()).isTableExists(schema, tableName);
		} catch (SQLException e) {
			throw new SnipManagerException(url, e);
		}
	}

	@Override
	public String getContextId() {
		return connector.getContextId();
	}

	@Override
	public List<MetaTable> getTables(String shemaName) throws SnipManagerException {
		try {
			return new ServerTableManager(connector.getConnection()).getTables(shemaName);
		} catch (SQLException e) {
			throw new SnipManagerException(url, e);
		}
	}

	@Override
	public MetaTable getTableData(MetaTable table) throws SnipManagerException {
		try {
			return new ServerTableManager(connector.getConnection()).getTableData(table);
		} catch (SQLException e) {
			throw new SnipManagerException(url + TABLE2 + table.getName() + SCHEME + table.getSheme(), e);
		}
	}

	@Override
	public void showSQL(boolean isShow) {
		isShowSQL = isShow;
	}

	@Override
	public void showRetrieveSQL(boolean isShow) {
		isShowRetSQL = isShow;
	}

	@Override
	public IContextConnector getConnector() {
		return connector;
	}
}
